package gnss_plugs

import (
	"bytes"
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"time"
)

type GnssRinexEphIOWriter struct {
	IO            io.WriteCloser
	filename      string
	realfilename  string
	write_size    int64
	start_t       time.Time
	lastWrite_t   time.Time
	last_active_t time.Time // 最后访问的时间
	bb            utils.BytesBuilder
	checkMap      utils.GSyncMapGroup
}

func (w *GnssRinexEphIOWriter) Close() {
	io := w.IO
	if io != nil {
		io.Close()
	}
	w.IO = nil
}

func (this *GnssRinexEphIOWriter) GetRealFileName() string {
	return this.realfilename
}

func (this *GnssRinexEphIOWriter) GetCacheSize() int64 {
	return int64(this.bb.Len())
}

func (this *GnssRinexEphIOWriter) GetLastWriteT() time.Time {
	return this.lastWrite_t
}

/*
true, 重复
false, 不重复
*/
func (this *GnssRinexEphIOWriter) CheckRepeat(eph *gnss.RtcmEph, t1 time.Time) bool {
	uuid := eph.MakeUUIDEx()
	//if eph.GetPrnStrCode() == "C14" {
	//	uuid = eph.MakeUUIDEx()
	//}
	v := this.checkMap.Get(uuid, nil)
	if v == nil {
		this.checkMap.Set(uuid, t1)
		return false
	}
	//if t1.Sub(v.(time.Time)).Hours() > 1 {
	//	this.checkMap.Set(uuid, t1)
	//	return false
	//}

	return true
}

func (w *GnssRinexEphIOWriter) CheckIOWrite() (error, io.WriteCloser) {
	if w.IO == nil { // IO不存在, 尝试打开文件
		//if len(w.realfilename) > 0 { // 旧的文件, 直接追加
		f1, err := os.OpenFile(w.filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			w.realfilename = ""
			return err, nil
		} else {
			w.IO = f1
			w.write_size, _ = utils.FileSize(w.filename)
			w.realfilename = w.filename
			return nil, w.IO
		}
		//}
		//if len(w.realfilename) == 0 { // 新文件
		//	f1, fname, err := utils.CreateIfExistsThenRename(w.filename)
		//	if err != nil {
		//		return err, nil
		//	}
		//	w.realfilename = fname
		//	w.IO = f1
		//	w.write_size = 0
		//	return nil, f1
		//}
	}
	return nil, w.IO
}

/***
 */
type GnssRinexEph3xWriterBase struct {
	Id                string
	bodyCnt           int
	LastWriter        *GnssRinexEphIOWriter // 最后一个写入的文件
	lastWriteWarning  string
	lastWriteFileName string
	writerCacheSize   int // 多大开始写文件
	writerCleanSize   int // 多少时开始丢弃文件

	// 如果不为255, 则进行过滤
	Sys     byte
	Filefmt string

	ionosMap map[string]*Rinex2XIonosRec

	writerMap utils.GSyncMapGroup

	OnGetEphTimeFunc func(rec *gnss.RtcmEph) time.Time
}

func (this *GnssRinexEph3xWriterBase) onCreate() {
	this.ionosMap = make(map[string]*Rinex2XIonosRec)
	this.Sys = 255
	this.writerCacheSize = 1024      // 1K 开始写入文件
	this.writerCleanSize = 1024 * 10 // 10K 开始丢弃文件
}

func (this *GnssRinexEph3xWriterBase) GetWriteCacheSize() int {
	return this.writerCacheSize
}

func (this *GnssRinexEph3xWriterBase) ConfigWriteCacheSize(v int) {
	this.writerCacheSize = v
	this.writerCleanSize = v * 5
}

func (this *GnssRinexEph3xWriterBase) GetBodyCnt() int {
	return this.bodyCnt
}

func (this *GnssRinexEph3xWriterBase) getFileName(curT time.Time) string {
	str := this.Filefmt
	if curT.IsZero() {
		curT = time.Now().UTC()
	}
	str = utils.ParseTimeFmt(str, curT)
	return str
}

func (this *GnssRinexEph3xWriterBase) LastWriteFile() string {
	return this.lastWriteFileName
}

func (this *GnssRinexEph3xWriterBase) WriteCache2File() {
	this.writerMap.Range(func(key, value interface{}) bool {
		rec := value.(*GnssRinexEphIOWriter)
		this.writerCache(rec)
		return true
	})
}

func (this *GnssRinexEph3xWriterBase) CheckWriteAndGetLatestFile() string {
	wr := this.LastWriter
	if wr != nil {
		this.writerCache(wr)
		return wr.realfilename
	}
	return ""
}

func (this *GnssRinexEph3xWriterBase) RangeWriterMap(cb func(rec *GnssRinexEphIOWriter) bool) {
	this.writerMap.Range(func(key, value interface{}) bool {
		rec := value.(*GnssRinexEphIOWriter)
		return cb(rec)
	})
}

func (this *GnssRinexEph3xWriterBase) Close() {
	this.writerMap.Range(func(key, value interface{}) bool {
		rec := value.(*GnssRinexEphIOWriter)
		this.writerCache(rec)
		rec.Close()
		this.writerMap.Remove(key)
		return true
	})
}

/**
 * 将临时文件写入到一个文件
 */
func (this *GnssRinexEph3xWriterBase) writeHeader(t1 time.Time, bb *utils.BytesBuilder) {

	// first line
	bb.WriteString(utils.AddSuffixForWidth("     3.03           N: GNSS NAV DATA    M: Mixed", 60, " "))
	bb.WriteString("RINEX VERSION / TYPE\n")
	bb.WriteString("     4     4   574     1 BDT                                LEAP SECONDS\n")
	bb.WriteString("    18    18  1930     1 GPS                                LEAP SECONDS\n")
	bb.WriteString("    18    18  1930     1 GAL                                LEAP SECONDS\n")
	bb.WriteString(utils.AddSuffixForWidth(RINEX_TransVer, 20, " "))
	bb.WriteString(utils.AddSuffixForWidth(RINEX_CopyRight, 20, " "))
	bb.WriteString(utils.AddSuffixForWidth(t1.Format("20060102 150405")+" UTC", 20, " "))
	bb.WriteString("PGM / RUN BY / DATE\n")

	bb.WriteString(this.getIonosHeader())
	bb.WriteString(utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n")
}

func (this *GnssRinexEph3xWriterBase) writerCache(writer *GnssRinexEphIOWriter) {
	e1, wio := writer.CheckIOWrite()
	if e1 != nil {
		this.lastWriteWarning = fmt.Sprintf("[%s]写入文件[%s]异常:%s", utils.NowString(), writer.realfilename, e1.Error())
		return
	}

	if writer.write_size == 0 {
		var hb utils.BytesBuilder
		this.writeHeader(writer.start_t, &hb)
		n, e1 := wio.Write(hb.Bytes())
		if e1 != nil {
			this.lastWriteWarning = fmt.Sprintf("[%s]写入文件[%s]异常:%s", utils.NowString(), writer.realfilename, e1.Error())
			return
		}
		writer.write_size += int64(n)
	}
	n, e1 := wio.Write(writer.bb.Bytes())
	if e1 != nil {
		this.lastWriteWarning = fmt.Sprintf("[%s]写入文件[%s]异常:%s", utils.NowString(), writer.realfilename, e1.Error())
		return
	}
	this.lastWriteFileName = writer.realfilename
	writer.bb.Reset()
	writer.write_size += int64(n)
	writer.lastWrite_t = time.Now()
}

func (this *GnssRinexEph3xWriterBase) CheckReleaseWriter() {
	this.writerMap.Range(func(key, value interface{}) bool {
		rec := value.(*GnssRinexEphIOWriter)
		if !rec.last_active_t.IsZero() && time.Since(rec.last_active_t).Hours() > 24 && time.Since(rec.last_active_t).Hours() > 24 {
			this.writerCache(rec)
			this.writerMap.Remove(key)
		}
		return true
	})
}

func (this *GnssRinexEph3xWriterBase) writeEph(filename string, t1 time.Time, eph *gnss.RtcmEph) {
	writer := this.checkWriter(filename)
	this.LastWriter = writer
	if writer.start_t.IsZero() {
		writer.start_t = t1
	}
	writer.last_active_t = time.Now()
	if writer.CheckRepeat(eph, t1) {
		return
	}
	if writer.bb.Len() >= this.writerCleanSize && this.writerCleanSize > 0 { // 清理
		writer.bb.Reset()
	}
	writer.bb.AppendStr(eph.GetRinexStr())
	if writer.bb.Len() > this.writerCacheSize {
		this.writerCache(writer)
	}

}

/*
*
根据obsFile获取Writer对象
*/
func (this *GnssRinexEph3xWriterBase) checkWriter(filename string) *GnssRinexEphIOWriter {
	utils.ForceCreateFilePath(filename)
	w := this.writerMap.LoadOrStoreFunc(filename, func() interface{} {
		rval := &GnssRinexEphIOWriter{filename: filename}
		return rval
	}, nil).(*GnssRinexEphIOWriter)

	return w

}

func (this *GnssRinexEph3xWriterBase) checkGetIonosRec(hcStr string) *Rinex2XIonosRec {
	itm := this.ionosMap[hcStr]
	if itm == nil {
		itm = &Rinex2XIonosRec{}
		itm.timeid = hcStr
		this.ionosMap[hcStr] = itm
	}
	return itm
}

func (this *GnssRinexEph3xWriterBase) getIonosHeader() string {
	headers := make([]*Rinex2XIonosRec, 0, len(this.ionosMap))
	for _, v := range this.ionosMap {
		headers = append(headers, v)
	}

	sort.Slice(headers, func(i, j int) bool {
		return strings.Compare(headers[i].timeid, headers[j].timeid) < 0
	})

	str := ""
	for _, rec := range headers {
		str += rec.igmas31 + rec.igmas32 + rec.igmas33 + rec.igmas34
	}

	return str
}

func (this *GnssRinexEph3xWriterBase) ProcessIgmasIonos(rec *gnss.GnssRtcmData) {
	if utils.IndexInt(rec.MsgId, 31, 32, 33, 34) >= 0 { // 处理Igmas电离层
		hcStr := utils.ToHourChr(time.Now().UTC())
		if !rec.RefUtc.IsZero() {
			hcStr = utils.ToHourChr(rec.RefUtc)
		}

		if rec.MsgId == 31 {
			if rec31, ok := rec.Data.(*gnss.Gnss31Bds); ok {
				itm := this.checkGetIonosRec(hcStr)
				str := fmt.Sprintf("BDSA %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec31.BDS_Alpha0, 4), FPrintRinexEV(rec31.BDS_Alpha1, 4),
					FPrintRinexEV(rec31.BDS_Alpha2, 4), FPrintRinexEV(rec31.BDS_Alpha3, 4), hcStr)
				str += fmt.Sprintf("BDSB %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec31.BDS_Beta0, 4), FPrintRinexEV(rec31.BDS_Beta1, 4),
					FPrintRinexEV(rec31.BDS_Beta2, 4), FPrintRinexEV(rec31.BDS_Beta3, 4), hcStr)
				itm.igmas31 = str

				//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
			}
		} else if rec.MsgId == 32 {
			if bds32Rec, ok := rec.Data.(*gnss.Gnss32Bds); ok {
				rec32 := bds32Rec

				str := fmt.Sprintf("BDS1 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha1, 4), FPrintRinexEV(rec32.BDS_Alpha2, 4),
					FPrintRinexEV(rec32.BDS_Alpha3, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)
				str += fmt.Sprintf("BDS2 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha4, 4), FPrintRinexEV(rec32.BDS_Alpha5, 4),
					FPrintRinexEV(rec32.BDS_Alpha6, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)
				str += fmt.Sprintf("BDS3 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha7, 4), FPrintRinexEV(rec32.BDS_Alpha8, 4),
					FPrintRinexEV(rec32.BDS_Alpha9, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)

				itm := this.checkGetIonosRec(hcStr)
				itm.igmas32 = str
				//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
			}
		} else if rec.MsgId == 33 {
			if rec33, ok := rec.Data.(*gnss.IgmasIonos); ok {

				str := fmt.Sprintf("GPSA %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec33.Alpha0, 4), FPrintRinexEV(rec33.Alpha1, 4),
					FPrintRinexEV(rec33.Alpha2, 4), FPrintRinexEV(rec33.Alpha3, 4), hcStr)
				str += fmt.Sprintf("GPSB %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec33.Beta0, 4), FPrintRinexEV(rec33.Beta1, 4),
					FPrintRinexEV(rec33.Beta2, 4), FPrintRinexEV(rec33.Beta3, 4), hcStr)

				itm := this.checkGetIonosRec(hcStr)
				itm.igmas33 = str
				//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
			}
		} else if rec.MsgId == 34 {
			if rec34, ok := rec.Data.(*gnss.IgmasGalileoIonos); ok {
				str := fmt.Sprintf("GAL  %s%s%s%s %s     IONOSPHERIC CORR\n",
					FPrintRinexEV(rec34.Ai0, 4), FPrintRinexEV(rec34.Ai1, 4),
					FPrintRinexEV(rec34.Ai2, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)

				itm := this.checkGetIonosRec(hcStr)
				itm.igmas34 = str
				//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
			}
		}
	}
}

func (this *GnssRinexEph3xWriterBase) OnRecvGnssRecCallBack(sender interface{}, channelid string, subscribeid interface{}, data interface{}, tag interface{}, isBreak *bool) bool {
	if data == nil {
		this.Close()
		return true
	}
	rtcmRec, ok := data.(*gnss.GnssRtcmData)
	if ok {
		this.InputRtcmData(sender, rtcmRec)
	}
	return true
}

func (this *GnssRinexEph3xWriterBase) InputRtcmData(sender interface{}, rec *gnss.GnssRtcmData) {

	if rec.TypeId == gnss.TYPE_EPH {
		if this.Sys != 255 && rec.Sys != this.Sys {
			return
		}
		if eph, ok := rec.Data.(*gnss.RtcmEph); ok {
			err1 := eph.CheckSatIdInvalid()
			if err1 != nil {
				utils.Log.Warnf("[RTCM数据错误]:%s\r\n", err1.Error(), utils.BufToHexStr(rec.RawBuf, -1, " "))
			}

			t1 := eph.Time
			if this.OnGetEphTimeFunc != nil {
				t1 = this.OnGetEphTimeFunc(eph)
			}

			filename := this.getFileName(t1)
			this.bodyCnt++
			this.writeEph(filename, t1, eph)
		}

		return
	}

	this.ProcessIgmasIonos(rec)
}
